package com.niiwoo.civet.trade.service.local.refund;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

import com.niiwoo.civet.trade.enums.ProjectTypeEnum;
import org.apache.commons.collections.CollectionUtils;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.niiwoo.civet.base.enums.ChannelCodeEnum;
import com.niiwoo.civet.base.enums.RefundTypeEnum;
import com.niiwoo.civet.trade.dao.entity.Project;
import com.niiwoo.civet.trade.dao.entity.ProjectBorrowerPlanNew;
import com.niiwoo.civet.trade.dao.entity.ProjectBorrowerSummary;
import com.niiwoo.civet.trade.dao.mapper.ProjectBorrowerPlanNewMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectBorrowerSummaryMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectMapperExt;
import com.niiwoo.civet.trade.dto.BorrowerRepaymentAmount;
import com.niiwoo.civet.trade.dto.ProjectBorrowerPlanNewDetailDTO;
import com.niiwoo.civet.trade.dto.newRefund.RefundStateDTO;
import com.niiwoo.civet.trade.enums.NotifyTypeEnum;
import com.niiwoo.civet.trade.enums.ProjectFromSourceEnum;
import com.niiwoo.civet.trade.enums.RepayStatusEnum;
import com.niiwoo.civet.trade.service.local.newRefund.common.RefundCommonService;
import com.niiwoo.civet.trade.service.local.newRefund.refundDeal.core.FeeCalcForRefundService;
import com.niiwoo.civet.trade.service.local.notify.Notifiers;
import com.niiwoo.civet.trade.utils.RepaymentUtil;
import com.niiwoo.civet.user.dto.request.AppButtonDTO;
import com.niiwoo.civet.user.dto.request.JPushExtDTO;
import com.niiwoo.civet.user.dto.request.UserLetterRequestDTO;
import com.niiwoo.civet.user.enums.JPushEnum;
import com.niiwoo.civet.user.enums.MessageTemplateKeyEnum;
import com.niiwoo.civet.user.enums.SMSTemplateKeyEnum;
import com.niiwoo.civet.user.enums.UserLetterTypeEnum;
import com.niiwoo.civet.user.service.SendSmsDubboService;
import com.niiwoo.civet.user.service.UserLetterDubboService;

import lombok.extern.slf4j.Slf4j;

/**
 * 还款消息提醒 
 *
 * @author: kevin.jia
 * @version 1.0.0
 */
@Slf4j
@Service
public class RepaymentRemindLocalService {

	@Autowired
	private ProjectMapperExt projectMapper;

	@Autowired
	private ProjectBorrowerSummaryMapperExt projectBorrowerSummaryMapperExt;
	
	@Autowired
	private ProjectBorrowerPlanNewMapperExt projectBorrowerPlanNewMapper;

	@Autowired
	private Notifiers notifiers;
	
	@Autowired
	private RefundCommonService refundCommonService;
	
	@Autowired
	private CommonLocalService commonLocalService;
	
	@Autowired
	private FeeCalcForRefundService feeCalcForRefundService;

	@Reference(version = "1.0.0")
	private UserLetterDubboService userLetterDubboService;

	@Reference(version = "1.0.0")
	private SendSmsDubboService sendSmsDubboService;

	@Value("${h5-borrow-detail-url}")
	private String h5BorrowDetailUrl;
	
	/** 默认一次处理记录数 */
	private static final Integer DEFAULT_LIMIT_NUM = 100;
	
	/** 默认还款前N天需要提醒还款 */
	private static final List<Integer> DIFFER_DAYS_LIST = Arrays.asList(1, 3, 7);

	/**
	 * 还款消息提醒
	 */
	public void sendNormalRepaymentRemindMessage() {
		List<RepayStatusEnum> repayStatusEnumList = Arrays.asList(RepayStatusEnum.NORMAL);
		int offset = 0;
		while (true) {
			// 查询待还款数据
			List<ProjectBorrowerSummary> projectBorrowerSummaryList = projectBorrowerSummaryMapperExt.selectByRepayStatusList(repayStatusEnumList, DIFFER_DAYS_LIST, offset, DEFAULT_LIMIT_NUM);
			log.info("batchSendRepaymentRemindMessage.projectBorrowerSummaryList={}", JSON.toJSONString(projectBorrowerSummaryList));
			
			// 为空跳出
			if (CollectionUtils.isEmpty(projectBorrowerSummaryList)) {
				break;
			}
			
			// 调用通知发送提配消息
			for(ProjectBorrowerSummary summaryDTO : projectBorrowerSummaryList) {
				try {
					Project project = projectMapper.selectByPrimaryKey(summaryDTO.getProjectId());
					if(ProjectTypeEnum.ORG_SPEEDLOAN_NEW.getValue().equals(project.getType())){
						continue;
					}

					notifiers.notifyUser(NotifyTypeEnum.JS016, summaryDTO);
				} catch (Exception e) {
					log.error("sendRepaymentRemindMessage发送还款提醒消息异常，summaryDTO={}", JSON.toJSONString(summaryDTO), e);
				}
			}
			offset += DEFAULT_LIMIT_NUM;
		}
	}
	
	/**
	 * 逾期消息提醒
	 */
	public void sendOverdueRepaymentRemindMessage() {
		int offset = 0;
		while (true) {
			List<ProjectBorrowerSummary> projectBorrowerSummaryList = projectBorrowerSummaryMapperExt.queryMyLoanOverdueProjectListByPage(offset, DEFAULT_LIMIT_NUM);
			log.info("batchSendRepaymentOverdueMessage.projectBorrowerSummaryList={}", JSON.toJSONString(projectBorrowerSummaryList));

			// 为空跳出
			if (CollectionUtils.isEmpty(projectBorrowerSummaryList)) {
				break;
			}
			
			// 发送还款逾期消息
			for(ProjectBorrowerSummary summaryDTO : projectBorrowerSummaryList) {
				try {
					Project project = projectMapper.selectByPrimaryKey(summaryDTO.getProjectId());
					if(ProjectTypeEnum.ORG_SPEEDLOAN_NEW.getValue().equals(project.getType())){
						continue;
					}

					sendRepaymentOverdueMessage(summaryDTO);
				} catch (Exception e) {
					log.error("sendRepaymentRemindMessage发送还款逾期消息异常，summaryDTO={}", JSON.toJSONString(summaryDTO), e);
				}
			}
			offset += DEFAULT_LIMIT_NUM;
		}
	}
	
	/**
	 * 逾期消息推送
	 * 
	 * @param summaryDTO
	 */
	private void sendRepaymentOverdueMessage(ProjectBorrowerSummary summaryDTO) {
		// 站内信、极光推送
		sendRepaymentOverdueLetterMessage(summaryDTO);
		// 短信推送
		sendRepaymentOverdueSmsMessage(summaryDTO);
	}

	/**
	 * 逾期站内信、极光推送
	 * 
	 * @param summary
	 */
	private void sendRepaymentOverdueLetterMessage(ProjectBorrowerSummary summary) {
		
		String userId = summary.getBorrowerUserId();
		Long projectId = summary.getProjectId();
		String projectTitle = summary.getProjectTitle();
		String periodString = summary.getCurrentPeriod() + "/" + summary.getTotalPeriod();
		Byte repayStatus = summary.getRepayStatus();
		
		int overdueDays = RepaymentUtil.getOverdueDay(summary.getRefundDate(), new Date()); // 逾期天数

		MessageTemplateKeyEnum.LetterTemplateKeyEnum letterTemplateKeyEnum = null;
		MessageTemplateKeyEnum.JPushTemplateKeyEnum jPushTemplateKeyEnum = null;
		List<Object> letterTemplateParamList = new ArrayList<>();
		List<Object> jPushTemplateParamList = new ArrayList<>();
		
		// 计算金额，应还金额小于0直接跳过（异常数据兼容）
		BorrowerRepaymentAmount borrowerRepaymentAmount = getBorrowerRepaymentAmount(summary);
		if (null == borrowerRepaymentAmount || borrowerRepaymentAmount.getShouldRefundedAmount().compareTo(BigDecimal.ZERO) <= 0) {
			return;
		}
		
		// 逾期
		if (RepayStatusEnum.OVERDUE.getCode().equals(repayStatus)) {
			letterTemplateKeyEnum = MessageTemplateKeyEnum.LetterTemplateKeyEnum.BORROW_REPAYMENT_OVERDUE;
			jPushTemplateKeyEnum = MessageTemplateKeyEnum.JPushTemplateKeyEnum.BORROW_REPAYMENT_OVERDUE;
			letterTemplateParamList = Arrays.asList(projectId, projectTitle, periodString, borrowerRepaymentAmount.getAlreadyRefundedAmount(),
					borrowerRepaymentAmount.getRemainShouldRefundedAmount(), overdueDays);
			jPushTemplateParamList = Arrays.asList(projectTitle, periodString, borrowerRepaymentAmount.getAlreadyRefundedAmount(),
					borrowerRepaymentAmount.getRemainShouldRefundedAmount(), overdueDays);
		// 严重逾期（坏帐）			
		} else {
			letterTemplateKeyEnum = MessageTemplateKeyEnum.LetterTemplateKeyEnum.BORROW_REPAYMENT_SERIOUS_OVERDUE;
			jPushTemplateKeyEnum = MessageTemplateKeyEnum.JPushTemplateKeyEnum.BORROW_REPAYMENT_SERIOUS_OVERDUE;
			letterTemplateParamList = Arrays.asList(projectId, projectTitle, periodString);
			jPushTemplateParamList = Arrays.asList(projectTitle, periodString);
		}
		
		// 站内信推送参数
		UserLetterRequestDTO letterRequest = new UserLetterRequestDTO();
		letterRequest.setLetterTemplateEnum(letterTemplateKeyEnum);
		letterRequest.setLetterTemplateParamList(letterTemplateParamList);

		// 借款详情按钮以及参数配置
		List<AppButtonDTO> buttonList = new ArrayList<>();
		AppButtonDTO loanButton = new AppButtonDTO();
		loanButton.setLabel("借款详情");
		loanButton.setButtonType(AppButtonDTO.ButtonTypeEnum.MONMY_LOAN_DETAILEY_2);
		
		Map<String, Object> loanButtonParam = new HashMap<>();
		loanButtonParam.put("ProjectId", projectId);
		loanButtonParam.put("LoadServerUrl", h5BorrowDetailUrl + projectId);
		loanButton.setParams(loanButtonParam);
		buttonList.add(loanButton);
		
		// 项目群跳转按钮
		Project project = projectMapper.selectByPrimaryKey(summary.getProjectId());
		
		if (!ChannelCodeEnum.KAOLA.getChanelCode().equals(project.getChannelCode())) {
			AppButtonDTO messageButton = new AppButtonDTO();
			messageButton.setLabel("项目群");
			messageButton.setButtonType(AppButtonDTO.ButtonTypeEnum.PROJECT_GROUP);
			
			Map<String, Object> messageButtonParam = new HashMap<>();
			messageButtonParam.put("ProjectId", projectId);
			messageButton.setParams(messageButtonParam);
			buttonList.add(messageButton);
		}
		letterRequest.setLetterButtonList(buttonList);

		// 极光推送参数
		letterRequest.setJpushTemplateKeyEnum(jPushTemplateKeyEnum);
		letterRequest.setJpushTemplateParamList(jPushTemplateParamList);
		letterRequest.setToUserId(userId);
		letterRequest.setProjectId(projectId);
		letterRequest.setUserLetterTypeEnum(UserLetterTypeEnum.LETTER_AND_JPUSH);
		
		JPushExtDTO jPushExtDTO = new JPushExtDTO();
		jPushExtDTO.setRedirectPageEnum(JPushEnum.RedirectPageEnum.NOTICE_LIST_PAGE);
		letterRequest.setJPushExt(jPushExtDTO);
		
		log.info("还款逾期###消息通知###请求, project={}, data={}", summary.getProjectId(), JSON.toJSONString(letterRequest));
		CompletableFuture.runAsync(() -> userLetterDubboService.pushLetter(letterRequest))
				.whenComplete((success, throwable) -> {
					if (throwable != null) {
						log.error(String.format("还款逾期###消息通知###异常, project=%s, errorMessage=%s", projectId, throwable.getMessage()), throwable);
					}
				});
	}

	/**
	 * 逾期站内信、极光推送
	 * 
	 * @param summary
	 */	
	private void sendRepaymentOverdueSmsMessage(ProjectBorrowerSummary summary) {
		String userId = summary.getBorrowerUserId();
		Integer currentPeriod = summary.getCurrentPeriod();
		int overdueDays = Days.daysBetween(new LocalDate(summary.getRefundDate()), new LocalDate()).getDays(); // 逾期天数
		Byte repayStatus = summary.getRepayStatus();

		// 短信
		if (!this.isNeedSendSms(overdueDays, repayStatus)) {
			return;
		}

	
		
		BorrowerRepaymentAmount borrowerRepaymentAmount = getBorrowerRepaymentAmount(summary);
		BigDecimal alreadyRefundedAmount = borrowerRepaymentAmount.getAlreadyRefundedAmount(); // 已还金额
		BigDecimal remainShouldRefundedAmount = borrowerRepaymentAmount.getRemainShouldRefundedAmount(); // 剩余应还金额

		// 标的信息
		Project project = projectMapper.selectByPrimaryKey(summary.getProjectId());	
		boolean isCardLoan = project.getType().equals(com.niiwoo.civet.trade.enums.ProjectTypeEnum.CARD_NIU_LOAN.getValue());
		boolean isFormCardNiu = project.getFromSource().equals(ProjectFromSourceEnum.CARD_NIU.getType());

		if (isCardLoan && isFormCardNiu) {
			SMSTemplateKeyEnum smsTemplateKeyEnum = SMSTemplateKeyEnum.CARD_NIU_BORROW_REPAYMENT_OVERDUE;
			if (RepayStatusEnum.BADLOANS.getCode() == repayStatus.byteValue()) {
				smsTemplateKeyEnum = SMSTemplateKeyEnum.CARD_NIU_BORROW_REPAYMENT_SERIOUS_OVERDUE;
			}
			SMSTemplateKeyEnum finalSmsTemplateKeyEnum = smsTemplateKeyEnum;
			CompletableFuture.runAsync(() -> {
				sendSmsDubboService.sendSmsByUserId(userId, finalSmsTemplateKeyEnum.getKey(), Arrays.asList(summary.getProjectTitle(), currentPeriod + "/" + summary.getTotalPeriod(),
								alreadyRefundedAmount, remainShouldRefundedAmount, overdueDays));
			}).whenComplete((success, throwable) -> {
				if (throwable != null) {
					log.error(String.format("还款逾期###消息通知,短信###异常, userId=%s, errorMessage=%s", userId,
							throwable.getMessage()), throwable);
				}
			});
		} else {
			SMSTemplateKeyEnum smsTemplateKeyEnum = SMSTemplateKeyEnum.BORROW_REPAYMENT_OVERDUE;
			if (RepayStatusEnum.BADLOANS.getCode() == repayStatus.byteValue()) {
				smsTemplateKeyEnum = SMSTemplateKeyEnum.BORROW_REPAYMENT_SERIOUS_OVERDUE;
			}
			SMSTemplateKeyEnum finalSmsTemplateKeyEnum = smsTemplateKeyEnum;
			CompletableFuture.runAsync(() -> {sendSmsDubboService.sendSmsByUserId(userId, finalSmsTemplateKeyEnum.getKey(),
						Arrays.asList(summary.getProjectTitle(), currentPeriod + "/" + summary.getTotalPeriod(), alreadyRefundedAmount, remainShouldRefundedAmount, overdueDays));
			}).whenComplete((success, throwable) -> {
				if (throwable != null) {
					log.error(String.format("还款逾期###消息通知,短信###异常, userId=%s, errorMessage=%s", userId, throwable.getMessage()), throwable);
				}
			});
		}
	}

	private boolean isNeedSendSms(int overdueDays, Byte repayStatus) {
		if (RepayStatusEnum.BADLOANS.getCode() == repayStatus.byteValue()) {
			overdueDays = overdueDays - 30;
		}
		if (overdueDays <= 3) { // 前3天发送
			return true;
		}
		if (overdueDays <= 30 && (overdueDays - 3) % 3 == 0) { // 一个月内，每3天
			return true;
		}
		if (overdueDays > 30 && (overdueDays - 30) % 7 == 0) { // 严重逾期，一个月后每7天
			return true;
		}
		return false;
	}

	
	/**
	 * 获取逾期的还款计划金额
	 * 
	 * @param userId
	 * @param projectId
	 * @return
	 */
	private BorrowerRepaymentAmount getBorrowerRepaymentAmount(ProjectBorrowerSummary summary) {
		List<ProjectBorrowerPlanNew> projectBorrowerPlanNewList = Lists.newArrayList();
		if (RepayStatusEnum.OVERDUE.getCode().equals(summary.getRepayStatus())) {
			projectBorrowerPlanNewList = projectBorrowerPlanNewMapper.selectNeedRefundList(summary.getProjectId(), commonLocalService.getCurrentDate());
		} else {
			projectBorrowerPlanNewList = projectBorrowerPlanNewMapper.selectNeedRefundList(summary.getProjectId(), null);
		}
		
		if (CollectionUtils.isEmpty(projectBorrowerPlanNewList)) {
			return new BorrowerRepaymentAmount(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO);
		}
		
		// 计算当前期应还与已还
		BigDecimal alreadyRefundedAmount = BigDecimal.ZERO; // 当前期已还款
		BigDecimal shouldRefundedAmount = BigDecimal.ZERO; // 当前期应还款
		for (ProjectBorrowerPlanNew projectBorrowerPlanNew : projectBorrowerPlanNewList) {
			ProjectBorrowerPlanNewDetailDTO projectBorrowerPlanNewDetailDTO = refundCommonService.bulidProjectBorrowerPlanNewAmount(projectBorrowerPlanNew.getId());
			alreadyRefundedAmount = alreadyRefundedAmount.add(projectBorrowerPlanNewDetailDTO.getRefundedCapital())
					.add(projectBorrowerPlanNewDetailDTO.getRefundedInterest())
					.add(projectBorrowerPlanNewDetailDTO.getRefundedPenaltyFee())
					.add(projectBorrowerPlanNewDetailDTO.getRefundedManagementFee())
					.add(projectBorrowerPlanNewDetailDTO.getRefundedGuaranteeFee());
			shouldRefundedAmount = shouldRefundedAmount.add(projectBorrowerPlanNewDetailDTO.getCapital())
					.add(projectBorrowerPlanNewDetailDTO.getInterest())
					.add(projectBorrowerPlanNewDetailDTO.getManagementFee())
					.add(projectBorrowerPlanNewDetailDTO.getGuaranteeFee())
					.add(projectBorrowerPlanNewDetailDTO.getPenaltyFee());
		}

		// 调用计算服务计算当前待还金额
		RefundStateDTO refundStateDTO = feeCalcForRefundService.caculateAmountRepaymentForAPP(summary.getProjectId(), RefundTypeEnum.IMMEDIATELY);	
		BigDecimal remainShouldRefundedAmount = refundStateDTO.getAmount();

		return new BorrowerRepaymentAmount(alreadyRefundedAmount, shouldRefundedAmount, remainShouldRefundedAmount);
	}
}